#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemGameInfo.h"

#include "Network/PackageManager.h"
#include "Network/Packages/SubsystemTimePackage.h"

#include "Utils/Utils.h"
#include "Engine/MathUtils.hpp"

#include "GameSingleton.h"


using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    constexpr float DayDuration = 1200.0f;
    constexpr double OffsetFix = 0.3;
    constexpr float SunriseOffset = 0.25f;
    constexpr float DayOffset = 0.5f;
    constexpr float SunsetOffset = 0.75f;
    constexpr float NightOffset = 1.0f;

    float SubsystemTimeOfDay::getTimeOfDay()
    {
        switch (m_gameInfo->timeOfDayMode)
        {
        case TimeOfDayMode::Changing:
            return (float)MathUtils::Remainder(m_gameInfo->totalElapsedGameTime + (timeOfDayOffset + OffsetFix) * DayDuration, DayDuration) / DayDuration;
        case TimeOfDayMode::Day:
            return DayOffset;
        case TimeOfDayMode::Night:
            return NightOffset;
        case TimeOfDayMode::Sunrise:
            return SunriseOffset;
        case TimeOfDayMode::Sunset:
            return SunsetOffset;
        }
        return SunsetOffset;
    }

    double SubsystemTimeOfDay::getDay()
    {
        return (m_gameInfo->totalElapsedGameTime + (timeOfDayOffset + OffsetFix) * DayDuration) / DayDuration;
    }



    bool SubsystemTimeOfDay::periodicGameTimeEvent(double period, double offset)
	{
		double num = m_gameTime - offset;
		double num2 = MathUtils::Floor<float>(num / period) * period;
		if (num >= num2)
		{
			return num - m_gameTimeDelta < num2;
		}
		return false;
	}


    void SubsystemTimeOfDay::update(float dt)
    {
        m_gameTime += dt;
        m_gameTimeDelta = dt;
        if(periodicGameTimeEvent(1.0, 0.0))
        {
            GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<SubsystemTimePackage>(m_gameInfo->totalElapsedGameTime, timeOfDayOffset));
        }
    }

    void SubsystemTimeOfDay::load(const nlohmann::json &json)
    {
        m_gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        Utils::GetJsonValue<double>(json, timeOfDayOffset, "TimeOfDayOffset", 0.0);
    }

    void SubsystemTimeOfDay::save(nlohmann::json &json)
    {
        json["TimeOfDayOffset"] = timeOfDayOffset;
    }
    
    void SubsystemTimeOfDay::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "TimeOfDayOffset", "double", timeOfDayOffset);
    }



    static const std::string subsystemName = "TimeOfDay";
    const std::string& SubsystemTimeOfDay::getName() const
    {
        return subsystemName;
    }
    SubUpdateType SubsystemTimeOfDay::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }
}
